home *** CD-ROM | disk | FTP | other *** search
/ AOL File Library: 2,801 to 2,900 / aol-file-protocol-4400-2801-to-2900.zip / AOLDLs / C++ Files Library / CWASTE C++ text editing routi / CWASTE folder.sit / CWASTE folder / WASTE3.c < prev    next >
Text File  |  1994-07-20  |  34KB  |  1,355 lines

  1. // unit WASTE3;
  2.  
  3. // { WASTE PROJECT }
  4. // { Unit Three: Selecting; Highlighting; Scrolling &c. }
  5.  
  6. // { Copyright ⌐ 1993-1994 Marco Piovanelli }
  7. // { All Rights Reserved }
  8.  
  9. // conversion to C by Dan Crevier
  10.  
  11. #include "WASTEIntf.h"
  12. #include <QDOffscreen.h>
  13.  
  14.  
  15. // { values for _WEArrowOffset action parameter: }
  16. // { plain arrow keys }
  17. #define kGoLeft            0
  18. #define kGoRight        1
  19. #define kGoUp            2
  20. #define kGoDown            3
  21.  
  22. // { modifiers }
  23. #define kOption            4
  24. #define kCommand        8
  25.  
  26. // { option + arrow combos }
  27. #define kGoWordStart    kGoLeft + kOption
  28. #define kGoWordEnd        kGoRight + kOption
  29. #define kGoTextStart    kGoUp + kOption
  30. #define kGoTextEnd        kGoDown + kOption
  31.  
  32. // { command + arrow combos }
  33. #define kGoLineStart    kGoLeft + kCommand
  34. #define kGoLineEnd        kGoRight + kCommand
  35. #define kGoPageStart    kGoUp + kCommand
  36. #define kGoPageEnd        kGoDown + kCommand
  37.  
  38.  
  39. void ClearHiliteBit(void)
  40. {
  41.     LMSetHiliteMode(LMGetHiliteMode() & 0x7f);
  42. }
  43.  
  44. Boolean SLPixelToChar(LinePtr pLine, WERunAttributesPtr pAttrs, Ptr pSegment,
  45.                     long segmentStart, long segmentLength, JustStyleCode styleRunPosition,
  46.                     void *callbackData);
  47.  
  48. Boolean SLPixelToChar(LinePtr pLine, WERunAttributesPtr pAttrs, Ptr pSegment,
  49.                     long segmentStart, long segmentLength, JustStyleCode styleRunPosition,
  50.                     void *callbackData)
  51. {
  52.     struct SLPixelToCharData *p = (struct SLPixelToCharData *) callbackData;
  53.     WEPtr pWE = p->pWE;
  54.     Fixed slop;
  55.     Fixed width;
  56. //    long adjustment;
  57.     
  58.     // { if this is the first style run on the line, subtract pen indent from pixelWidth }
  59.     if (styleRunPosition <= smLeftStyleRun) 
  60.     {
  61. //        adjustment = _WECalcPenIndent(pLine->lineSlop, pWE->alignment);
  62. //        adjustment = BSL(adjustment, 16);
  63.         p->pixelWidth = p->pixelWidth - BSL(_WECalcPenIndent(pLine->lineSlop, pWE->alignment), 16);
  64.     }
  65.     
  66.     // { strip trailing spaces if this is the last segment on the line }
  67.     if (!(styleRunPosition & 1)) 
  68.     {
  69.         segmentLength = VisibleLength(pSegment, segmentLength);
  70.     }
  71.     
  72.     // { calculate slop for this text segment (justified text only) }
  73.     if (pWE->alignment == weJustify) 
  74.     {
  75.         slop = FixMul(PortionLine(pSegment, segmentLength, styleRunPosition,
  76.             *(Point *)(&kOneToOneScaling), *(Point *)(&kOneToOneScaling)),
  77.             pLine->lineJustAmount);
  78.     }
  79.     else
  80.     {
  81.         slop = 0;
  82.     }
  83.     
  84.     // { call PixelToChar for this segment }
  85.     p->offset = segmentStart + NPixel2Char(pSegment, segmentLength, slop, p->pixelWidth,
  86.         (Boolean *)p->edge, &width, styleRunPosition, *(Point *)(&kOneToOneScaling),
  87.         *(Point *)(&kOneToOneScaling));
  88.  
  89.     // { update pixelWidth for next iteration }
  90.     p->pixelWidth = width;
  91.  
  92.     // { if pixelWidth has gone negative, we're finished; otherwise go to next run }
  93.     return (p->pixelWidth < 0);
  94. }
  95.  
  96. pascal long WEGetOffset(LongPt *thePoint, char *edge, WEHandle hWE)
  97. {
  98.     // { given a long point in local coordinates, }
  99.     // { find the text offset corresponding to the nearest glyph }
  100.  
  101.     WEPtr pWE;
  102.     long lineIndex;
  103.     Fixed pixelWidth;
  104.     Boolean saveWELock;
  105.     long retval;
  106.     struct SLPixelToCharData callbackData;
  107.     LongPt tempPoint = *thePoint; // so we don't change original point
  108.  
  109.     // { lock the WE record }
  110.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  111.     pWE = *hWE;
  112.  
  113.     // { offset thePoint so that it is relative to the top left corner of the destination rectangle }
  114.     tempPoint.v = tempPoint.v - pWE->destRect.top;
  115.     tempPoint.h = tempPoint.h - pWE->destRect.left;
  116.  
  117.     // { if the point is above the destination rect, return zero }
  118.     if (tempPoint.v < 0) 
  119.     {
  120.         retval = 0;
  121.         *edge = kTrailingEdge;
  122.     }
  123.     else
  124.     {
  125.         // { if the point is below the last line, return last char offset }
  126.         if (tempPoint.v >= WEGetHeight(0, LONG_MAX, hWE)) 
  127.         {
  128.             retval = pWE->textLength;
  129.             *edge = kLeadingEdge;
  130.         }
  131.         else
  132.         {
  133.             // { find the line index corresponding to the vertical pixel offset }
  134.             lineIndex = _WEPixelToLine(tempPoint.v, hWE);
  135.  
  136.             // { express the horizontal pixel offset as a Fixed value }
  137.             pixelWidth = BSL(tempPoint.h, 16);
  138.  
  139.             // { walk through the segments on this line calling PixelToChar }
  140.             callbackData.pWE = pWE;
  141.             callbackData.pixelWidth = pixelWidth;
  142.             callbackData.edge = edge;
  143.             callbackData.offset = 0;
  144.             _WESegmentLoop(lineIndex, lineIndex, SLPixelToChar, (void *)&callbackData, hWE);
  145.             retval = callbackData.offset;
  146.         }
  147.     }
  148.     
  149.     // { unlock the WE record }
  150.     _WESetHandleLock((Handle)hWE, saveWELock);
  151.  
  152.     return retval;
  153. }
  154.  
  155. Boolean SLCharToPixel(LinePtr pLine, WERunAttributesPtr pAttrs, Ptr pSegment,
  156.                         long segmentStart, long segmentLength, JustStyleCode styleRunPosition,
  157.                         void *callbackData);
  158.  
  159. Boolean SLCharToPixel(LinePtr pLine, WERunAttributesPtr pAttrs, Ptr pSegment,
  160.                         long segmentStart, long segmentLength, JustStyleCode styleRunPosition,
  161.                         void *callbackData)
  162. {
  163.     struct SLCharToPixelData *p = (struct SLCharToPixelData *) callbackData;
  164.     WEPtr pWE = p->pWE;
  165.     Fixed slop;
  166.     Boolean leadingEdge;
  167.  
  168.     // { if this is the first style run on the line, add pen indent to thePoint.h }
  169.     if (styleRunPosition <= smLeftStyleRun) 
  170.     {
  171.         p->thePoint->h = p->thePoint->h + _WECalcPenIndent(pLine->lineSlop, pWE->alignment);
  172.     }
  173.     
  174.     // { calculate slop for this text segment (justified text only) }
  175.     if (pWE->alignment == weJustify) 
  176.     {
  177.         slop = FixMul(PortionLine(pSegment, segmentLength, styleRunPosition,
  178.             *(Point *)(&kOneToOneScaling), *(Point *)(&kOneToOneScaling)),
  179.             pLine->lineJustAmount);
  180.     }
  181.     else
  182.     {
  183.         slop = 0;
  184.     }
  185.     
  186.     // { call CharToPixel to get width of segment up to specified offset }
  187.     leadingEdge = smHilite;
  188.     p->thePoint->h = p->thePoint->h + NChar2Pixel(pSegment, segmentLength, slop,
  189.         p->offset - segmentStart, smHilite, styleRunPosition,
  190.         *(Point *)(&kOneToOneScaling), *(Point *)(&kOneToOneScaling));
  191.  
  192.     // { drop out of loop when we reach offset }
  193.     return (p->offset < segmentStart + segmentLength);
  194. }
  195.  
  196. pascal void WEGetPoint(long offset, LongPt *thePoint, short *lineHeight, WEHandle hWE)
  197. {
  198.     // { given a byte offset into the text, find the corresponding glyph position }
  199.     // { this routine is useful for highlighting the text and for positioning the caret }
  200.  
  201.     WEPtr pWE;
  202.     LinePeek pLine;
  203.     long lineIndex;
  204.     Boolean saveWELock;
  205.     struct SLCharToPixelData callbackData;    
  206.     
  207.     // { lock the WE record }
  208.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  209.     pWE = *hWE;
  210.  
  211.     // { the base point is the top left corner of the destination rectangle }
  212.     *thePoint = *(LongPt *)&pWE->destRect.top;
  213.  
  214.     // { first of all find the line on which the glyph lies }
  215.     lineIndex = _WEOffsetToLine(offset, hWE);
  216.  
  217.     // { calculate the vertical coordinate and the line height }
  218.     pLine = (LinePeek)&((*pWE->hLines)[lineIndex]);
  219.     thePoint->v = thePoint->v + pLine->first.lineOrigin;
  220.     *lineHeight = pLine->second.lineOrigin - pLine->first.lineOrigin;
  221.  
  222.     if ((offset == pWE->textLength) && (WEGetChar(offset - 1, hWE) == '\r')) 
  223.     {
  224.         // { SPECIAL CASE: if offset is past the last character and }
  225.         // { the last character is a carriage return, return a point below the last line }
  226.  
  227.         thePoint->v = thePoint->v + *lineHeight;
  228.         thePoint->h = thePoint->h + _WECalcPenIndent(pWE->destRect.right - pWE->destRect.left, pWE->alignment);
  229.     }
  230.     else
  231.     {
  232.         callbackData.pWE = pWE;
  233.         callbackData.offset = offset;
  234.         callbackData.thePoint = thePoint;
  235.         // { to get the horizontal coordinate, walk through the style runs on this line }
  236.         _WESegmentLoop(lineIndex, lineIndex, SLCharToPixel, (void *)&callbackData, hWE);
  237.     }
  238.  
  239.     // { pin the horizontal coordinate to the destination rectangle }
  240.     thePoint->h = _WEPinInRange(thePoint->h, pWE->destRect.left, pWE->destRect.right);
  241.  
  242.     // { unlock the WE record }
  243.     _WESetHandleLock((Handle)hWE, saveWELock);
  244. }
  245.  
  246. pascal void WEFindLine(long offset, char edge, long *lineStart, long *lineEnd, WEHandle hWE)
  247. {
  248.     WEPtr pWE;
  249.     LineArrayPtr pLine;
  250.     
  251.     pWE = *hWE;
  252.     pLine = &(*pWE->hLines)[_WEOffsetToLine(offset, hWE)];
  253.     *lineStart = pLine[0].lineStart;
  254.     *lineEnd = pLine[1].lineStart;
  255. }
  256.  
  257. pascal short WEFindScriptRun(long offset, char edge, long *scriptRunStart, long *scriptRunEnd,
  258.                         WEHandle hWE)
  259. {
  260.     long index, saveIndex, saveRunEnd;
  261.     WERunInfo runInfo;
  262.     ScriptCode script1, script2;
  263.     short retval;
  264.     
  265.     if (BTST((*hWE)->flags, weFNonRoman))
  266.     { 
  267.         // { if more than one script is installed, limit the search of script run boundaries }
  268.         // { to a single line, for speed's sake }
  269.         WEFindLine(offset, edge, scriptRunStart, scriptRunEnd, hWE);
  270.  
  271.         // { find the style run the specified offset is in }
  272.         index = _WEOffsetToRun(offset, hWE);
  273.         _WEGetIndStyle(index, &runInfo, hWE);
  274.  
  275.         // { return the style run font as function result }
  276.         retval = runInfo.runAttrs.runStyle.tsFont;
  277.  
  278.         // { find the script code associated with this style run }
  279.         script1 = Font2Script(runInfo.runAttrs.runStyle.tsFont);
  280.  
  281.         // { save index and runInfo.runEnd for the second while loop }
  282.         saveIndex = index;
  283.         saveRunEnd = runInfo.runEnd;
  284.  
  285.         // { walk backwards across style runs preceding offset, looking for a script run boundary }
  286.         while (runInfo.runStart > *scriptRunStart)
  287.         {
  288.             index = index - 1;
  289.             _WEGetIndStyle(index, &runInfo, hWE);
  290.             script2 = Font2Script(runInfo.runAttrs.runStyle.tsFont);
  291.             if (script1 != script2) 
  292.             {
  293.                 *scriptRunStart = runInfo.runEnd;
  294.                 break;
  295.             }
  296.         }
  297.  
  298.         // { restore index and runInfo.runEnd }
  299.         index = saveIndex;
  300.         runInfo.runEnd = saveRunEnd;
  301.  
  302.         // { walk forward across style runs following offset, looking for a script run boundary }
  303.         while (runInfo.runEnd < *scriptRunEnd)
  304.         {
  305.             index = index + 1;
  306.             _WEGetIndStyle(index, &runInfo, hWE);
  307.             script2 = Font2Script(runInfo.runAttrs.runStyle.tsFont);
  308.             if (script1 != script2) 
  309.             {
  310.                 *scriptRunEnd = runInfo.runStart;
  311.                 break;
  312.             }
  313.         }
  314.     }
  315.     else
  316.     {
  317.         // { only the Roman script is enabled: the whole text constitutes one script run }
  318.         retval = systemFont;
  319.         *scriptRunStart = 0;
  320.         *scriptRunEnd = (*hWE)->textLength;
  321.     }
  322.     return retval;
  323. }
  324.  
  325. pascal void WEFindWord(long offset, char edge, long *wordStart, long *wordEnd, WEHandle hWE)
  326. {
  327.     WEPtr pWE;
  328.     GrafPtr port, savePort;
  329.     Handle hText;
  330.     long runStart, runEnd;
  331.     OffsetTable wordBreaks;
  332.     short saveFont;
  333.     Boolean saveTextLock;
  334.  
  335.     pWE = *hWE;
  336.     port = pWE->port;
  337.     hText = pWE->hText;
  338.  
  339.     // { set up the port }
  340.     GetPort(&savePort);
  341.     SetPort(port);
  342.  
  343.     // { find the script run the specified offset is in (words cannot straddle script boundaries) }
  344.     // { and set the port font to the specified script }
  345.     saveFont = port->txFont;
  346.     TextFont(WEFindScriptRun(offset, edge, &runStart, &runEnd, hWE));
  347.  
  348.     // { make sure we pass FindWord short values }
  349.     runStart = _WEPinInRange(runStart, offset - (SHRT_MAX / 2), offset);
  350.     runEnd = _WEPinInRange(runEnd, offset, offset + (SHRT_MAX / 2));
  351.  
  352.     // { lock the text }
  353.     saveTextLock = _WESetHandleLock(hText, true);
  354.  
  355.     // { call FindWord using the whole script run as a context }
  356.     FindWord(*hText + runStart, runEnd - runStart, offset - runStart, (Boolean)edge,
  357.         nil, wordBreaks);
  358.  
  359.     // { unlock the text }
  360.     _WESetHandleLock(hText, saveTextLock);
  361.  
  362.     // { restore font and port }
  363.     TextFont(saveFont);
  364.     SetPort(savePort);
  365.  
  366.     // { calculate wordStart and wordEnd relative to the beginning of the text }
  367.     *wordStart = runStart + wordBreaks[0].offFirst;
  368.     *wordEnd = runStart + wordBreaks[0].offSecond;
  369. }
  370.  
  371. void _WEDrawCaret(WEHandle hWE)
  372. {
  373.     WEPtr pWE;
  374.     LongPt thePoint;
  375.     Rect caretRect;
  376.     short caretHeight;
  377.     GrafPtr savePort;
  378.     RgnHandle saveClip;
  379.  
  380.     // { the WE record must be already locked }
  381.     pWE = *hWE;
  382.  
  383.     // { do nothing if we're not active }
  384.     if (!BTST(pWE->flags, weFActive)) 
  385.     {
  386.         return;
  387.     }
  388.  
  389.     // { find the caret position using WEGetPoint }
  390.     WEGetPoint(pWE->selStart, &thePoint, &caretHeight, hWE);
  391.     WELongPointToPoint(&thePoint, (Point *)&caretRect.top);
  392.     if (caretRect.left > pWE->destRect.left) 
  393.     {
  394.         caretRect.left = caretRect.left - 1;
  395.     }
  396.     
  397.     // { calculate caret rectangle }
  398.     caretRect.bottom = caretRect.top + caretHeight;
  399.     caretRect.right = caretRect.left + 1;
  400.  
  401.     // { set up the port }
  402.     GetPort(&savePort);
  403.     SetPort(pWE->port);
  404.  
  405.     // { clip to the view region }
  406.     saveClip = NewRgn();
  407.     GetClip(saveClip);
  408.     SetClip(pWE->viewRgn);
  409.  
  410.     // { draw the caret }
  411.     InvertRect(&caretRect);
  412.  
  413.     // { invert caretVisible }
  414.     pWE->flags = pWE->flags ^ (BSL(1, weFCaretVisible));
  415.  
  416.     // { update caretTime }
  417.     pWE->caretTime = TickCount();
  418.  
  419.     // { restore the clip region }
  420.     SetClip(saveClip);
  421.     DisposeRgn(saveClip);
  422.  
  423.     // { restore the port }
  424.     SetPort(savePort);
  425. }
  426.  
  427. pascal RgnHandle WEGetHiliteRgn(long rangeStart, long rangeEnd, WEHandle hWE)
  428. {
  429.     // { returns the hilite region corresponding to the specified range }
  430.     // { the caller is responsible for disposing of the returned region }
  431.     // { when it's finished with it }
  432.  
  433.     WEPtr pWE;
  434.     RgnHandle hiliteRgn;
  435.     LongRect selRect;
  436.     LongPt firstPoint, lastPoint;
  437.     short firstLineHeight, lastLineHeight;
  438.     Rect r;
  439.     GrafPtr savePort;
  440.     Boolean saveWELock;
  441.  
  442.     // { lock the WE record }
  443.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  444.     pWE = *hWE;
  445.  
  446.     // { set up the port }
  447.     GetPort(&savePort);
  448.     SetPort(pWE->port);
  449.  
  450.     // { make sure rangeStart comes before rangeEnd }
  451.     _WEReorder(&rangeStart, &rangeEnd);
  452.  
  453.     // { calculate pixel location corresponding to rangeStart }
  454.     WEGetPoint(rangeStart, &firstPoint, &firstLineHeight, hWE);
  455.  
  456.     // { calculate pixel location corresponding to rangeEnd }
  457.     WEGetPoint(rangeEnd, &lastPoint, &lastLineHeight, hWE);
  458.  
  459.     // { open a region: rects to be hilited will be accumulated in this }
  460.     OpenRgn();
  461.  
  462.     if (firstPoint.v == lastPoint.v) 
  463.     {
  464.         // { selection range encompasses only one line }
  465.         WESetLongRect(&selRect, firstPoint.h, firstPoint.v, lastPoint.h, lastPoint.v + lastLineHeight);
  466.         WELongRectToRect(&selRect, &r);
  467.         FrameRect(&r);
  468.     }
  469.     else
  470.     {
  471.         // { selection range encompasses more than one line }
  472.         // { hilite the first line }
  473.         WESetLongRect(&selRect, firstPoint.h, firstPoint.v, pWE->destRect.right, firstPoint.v + firstLineHeight);
  474.         WELongRectToRect(&selRect, &r);
  475.         FrameRect(&r);
  476.  
  477.         // { any lines between the first and the last one? }
  478.         if (firstPoint.v + firstLineHeight < lastPoint.v) 
  479.         {
  480.             // { hilite all the lines in-between }
  481.             WESetLongRect(&selRect, pWE->destRect.left, firstPoint.v + firstLineHeight, pWE->destRect.right, lastPoint.v);
  482.             WELongRectToRect(&selRect, &r);
  483.             FrameRect(&r);
  484.         }
  485.  
  486.         // { hilite the last line }
  487.         WESetLongRect(&selRect, pWE->destRect.left, lastPoint.v, lastPoint.h, lastPoint.v + lastLineHeight);
  488.         WELongRectToRect(&selRect, &r);
  489.         FrameRect(&r);
  490.     }
  491.  
  492.     // { copy the accumulated region into a new region }
  493.     hiliteRgn = NewRgn();
  494.     CloseRgn(hiliteRgn);
  495.  
  496.     // { restrict this region to the view region }
  497.     SectRgn(hiliteRgn, pWE->viewRgn, hiliteRgn);
  498.  
  499.     // { restore the port }
  500.     SetPort(savePort);
  501.  
  502.     // { unlock the WE record }
  503.     _WESetHandleLock((Handle)hWE, saveWELock);
  504.  
  505.     // { return the hilite region }
  506.     return hiliteRgn;
  507. }
  508.  
  509. void _WEHiliteRange(long rangeStart, long rangeEnd, WEHandle hWE)
  510. {
  511.     WEPtr pWE;
  512.     RgnHandle saveClip, auxRgn, hiliteRgn;
  513.     PenState savePen;
  514.     GrafPtr savePort;
  515.  
  516.     // { the WE record must be already locked }
  517.     pWE = *hWE;
  518.  
  519.     // { do nothing if the specified range is empty }
  520.     if (rangeStart == rangeEnd) 
  521.     {
  522.         return;
  523.     }
  524.  
  525.     // { set up the port }
  526.     GetPort(&savePort);
  527.     SetPort(pWE->port);
  528.  
  529.     // { create auxiliary regions }
  530.     saveClip = NewRgn();
  531.     auxRgn = NewRgn();
  532.  
  533.     // { restrict the clip region to the view rectangle }
  534.     GetClip(saveClip);
  535.     SectRgn(saveClip, pWE->viewRgn, auxRgn);
  536.     SetClip(auxRgn);
  537.  
  538.     // { get the hilite region corresponding to the specified range }
  539.     hiliteRgn = WEGetHiliteRgn(rangeStart, rangeEnd, hWE);
  540.  
  541.     // { hilite the region or frame it, depending on the setting of the active flag }
  542.     if (BTST(pWE->flags, weFActive))
  543.     {
  544.         ClearHiliteBit();
  545.         InvertRgn(hiliteRgn);
  546.     }
  547.     else if (BTST(pWE->flags, weFOutlineHilite)) 
  548.     {
  549.         GetPenState(&savePen);
  550.         PenNormal();
  551.         PenMode(patXor);
  552.         ClearHiliteBit();
  553.         FrameRgn(hiliteRgn);
  554.         SetPenState(&savePen);
  555.     }
  556.  
  557.     // { restore the clip region }
  558.     SetClip(saveClip);
  559.  
  560.     // { dispose of all regions }
  561.     DisposeRgn(saveClip);
  562.     DisposeRgn(auxRgn);
  563.     DisposeRgn(hiliteRgn);
  564.  
  565.     // { restore the port }
  566.     SetPort(savePort);
  567. }
  568.  
  569. pascal void WESetSelection(long selStart, long selEnd, WEHandle hWE)
  570. {
  571.     WEPtr pWE;
  572.     long oldSelStart, oldSelEnd;
  573.     Boolean saveWELock;
  574.  
  575.     // { lock the WE record }
  576.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  577.     pWE = *hWE;
  578.  
  579.     // { invalid the null style }
  580.     BCLR(pWE->flags, weFUseNullStyle);
  581.  
  582.     // { hide the caret if it's showing }
  583.     if (BTST(pWE->flags, weFCaretVisible))
  584.     {
  585.             _WEDrawCaret(hWE);
  586.     }
  587.     
  588.     // { range-check parameters }
  589.     selStart = _WEPinInRange(selStart, 0, pWE->textLength);
  590.     selEnd = _WEPinInRange(selEnd, 0, pWE->textLength);
  591.  
  592.     // { set the weFAnchorIsEnd bit if selStart > selEnd,  reorder the endpoints }
  593.     if (selStart > selEnd) 
  594.     {
  595.         BSET(pWE->flags, weFAnchorIsEnd);
  596.     }
  597.     else
  598.     {
  599.         BCLR(pWE->flags, weFAnchorIsEnd);
  600.     }
  601.     _WEReorder(&selStart, &selEnd);
  602.  
  603.     // { get old selection range }
  604.     oldSelStart = pWE->selStart;
  605.     oldSelEnd = pWE->selEnd;
  606.  
  607.     // { set new selection range }
  608.     pWE->selStart = selStart;
  609.     pWE->selEnd = selEnd;
  610.  
  611.     // { if we're active, invert the exclusive-OR between the old range and the new range. }
  612.     // { if we're inactive, this optimization can't be used because of outline highlighting. }
  613.     if (BTST(pWE->flags, weFActive))
  614.     { 
  615.         _WEReorder(&oldSelStart, &selStart);
  616.         _WEReorder(&oldSelEnd, &selEnd);
  617.         _WEReorder(&oldSelEnd, &selStart);
  618.     }
  619.  
  620.     _WEHiliteRange(oldSelStart, oldSelEnd, hWE);
  621.     _WEHiliteRange(selStart, selEnd, hWE);
  622.  
  623.     if (!BTST(pWE->flags, weFMouseTracking))
  624.     {
  625.         // { redraw the caret immediately, if the selection range is empty }
  626.         if (pWE->selStart == pWE->selEnd)
  627.         { 
  628.             _WEDrawCaret(hWE);
  629.         }
  630.         // { clear clickCount, unless we're tracking the mouse }
  631.         pWE->clickCount = 0;
  632.  
  633.         // { scroll the selection into view, unless we're tracking the mouse }
  634.         WESelView(hWE);
  635.  
  636.     }
  637.     
  638.     // { unlock the WE record }
  639.     _WESetHandleLock((Handle)hWE, saveWELock);
  640. }
  641.  
  642. pascal void WEClick(Point mouseLoc, short modifiers, long clickTime, WEHandle hWE)
  643. {
  644.     WEPtr pWE;
  645.     LongPt thePoint;
  646.     long offset, anchor;
  647.     long rangeStart, rangeEnd;
  648.     char edge;
  649.     Boolean isMultipleClick;
  650.     Boolean saveWELock;
  651.  
  652.     // { lock the WE record }
  653.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  654.     pWE = *hWE;
  655.  
  656.     // { hide the caret if it's showing }
  657.     if (BTST(pWE->flags, weFCaretVisible))
  658.     {
  659.         _WEDrawCaret(hWE);
  660.     }
  661.     
  662.     // { find click offset }
  663.     WEPointToLongPoint(mouseLoc, &thePoint);
  664.     offset = WEGetOffset(&thePoint, &edge, hWE);
  665.  
  666.     // { determine whether this click is part of a sequence }
  667.     isMultipleClick = ((clickTime < pWE->clickTime + GetDblTime()) &&
  668.         (offset == pWE->clickLoc));
  669.  
  670.     // { remember click time, click offset and edge value }
  671.     pWE->clickTime = clickTime;
  672.     pWE->clickLoc = offset;
  673.     pWE->clickEdge = edge;
  674.  
  675.     if ((modifiers & shiftKey) == 0) 
  676.     {
  677.         // { is this click part of a sequence or is it an isolate click? }
  678.         if (isMultipleClick) 
  679.         {
  680.             pWE->clickCount = pWE->clickCount + 1;
  681.             // { a double (triple) click creates an anchor-word (anchor-line) }
  682.             if (pWE->clickCount > 1) 
  683.             {
  684.                 WEFindLine(offset, edge, &pWE->anchorStart, &pWE->anchorEnd, hWE);
  685.             }
  686.             else
  687.             {
  688.                 WEFindWord(offset, edge, &pWE->anchorStart, &pWE->anchorEnd, hWE);
  689.             }
  690.             offset = pWE->anchorStart;
  691.         }
  692.         else
  693.         {
  694.             pWE->clickCount = 0;
  695.             anchor = offset;
  696.         }
  697.     }
  698.     else
  699.     {
  700.         // { if the shift key was down, use the old anchor offset found with the previous click }
  701.         if (BTST(pWE->flags, weFAnchorIsEnd)) 
  702.         {
  703.             anchor = pWE->selEnd;
  704.         }
  705.         else
  706.         {
  707.             anchor = pWE->selStart;
  708.         }
  709.     }
  710.     
  711.     // { set the weFMouseTracking bit while we track the mouse }
  712.     BSET(pWE->flags, weFMouseTracking);
  713.  
  714.     // { MOUSE TRACKING LOOP }
  715.     do
  716.     {
  717.         // { get text offset corresponding to mouse position }
  718.         WEPointToLongPoint(mouseLoc, &thePoint);
  719.         offset = WEGetOffset(&thePoint, &edge, hWE);
  720.  
  721.         // { if we're selecting words or lines, pin offset to a word or line boundary }
  722.         if (pWE->clickCount > 0) 
  723.         {
  724.             if (pWE->clickCount > 1) 
  725.             {
  726.                 WEFindLine(offset, edge, &rangeStart, &rangeEnd, hWE);
  727.             }
  728.             else
  729.             {
  730.                 WEFindWord(offset, edge, &rangeStart, &rangeEnd, hWE);
  731.             }
  732.             // { choose the word/line boundary and the anchor that are farthest away from each other }
  733.             if (offset > pWE->anchorStart) 
  734.             {
  735.                 anchor = pWE->anchorStart;
  736.                 offset = rangeEnd;
  737.             }
  738.             else
  739.             {
  740.                 offset = rangeStart;
  741.                 anchor = pWE->anchorEnd;
  742.             }
  743.         }
  744.         // { set the selection range from anchor point to current offset }
  745.         WESetSelection(anchor, offset, hWE);
  746.  
  747.         // { call the click loop callback, if any }
  748.         if (pWE->clickLoop != nil) 
  749.         {
  750.             if (((WEClickLoopProcPtr)pWE->clickLoop)(hWE) == false)
  751.             {
  752.                 break;
  753.             } 
  754.         }
  755.         // { update mouse position }
  756.         GetMouse(&mouseLoc);
  757.     } while (WaitMouseUp());
  758.  
  759.     // { clear the weFMouseTracking bit }
  760.     BCLR(pWE->flags, weFMouseTracking);
  761.  
  762.     // { redraw the caret immediately if the selection range is empty }
  763.     if (anchor == offset) 
  764.     {
  765.         _WEDrawCaret(hWE);
  766.     }
  767.     
  768.     // { unlock the WE record }
  769.     _WESetHandleLock((Handle)hWE, saveWELock);
  770. }
  771.  
  772. pascal void WESetAlignment(char alignment, WEHandle hWE)
  773. {
  774.     if ((alignment >= weFlushLeft) && (alignment <= weJustify)) 
  775.     {
  776.         if (alignment != (*hWE)->alignment) 
  777.         {
  778.             (*hWE)->alignment = alignment;
  779.             WEUpdate(nil, hWE);
  780.         }
  781.     }
  782. }
  783.  
  784. long _WEArrowOffset(short action, long offset, WEHandle hWE)
  785. {
  786.     // { given an action code (corresponding to a modifiers + arrow key combo) }
  787.     // { and an offset into the text, find the offset of the new caret position }
  788.  
  789.     LongPt thePoint;
  790.     long textLength, rangeStart, rangeEnd;
  791.     short lineHeight;
  792.     char edge;
  793.  
  794.     textLength = (*hWE)->textLength;
  795.     switch (action)
  796.     {
  797.         case kGoLeft: 
  798.             if (offset > 0)
  799.             {
  800.                 offset = offset - 1;
  801.                 if (WECharByte(offset, hWE) != smSingleByte) 
  802.                 {
  803.                     offset = offset - 1;
  804.                 }
  805.             }
  806.             break;
  807.             
  808.         case kGoRight: 
  809.             if (offset < textLength)
  810.             {
  811.                 offset = offset + 1;
  812.                 if (WECharByte(offset, hWE) != smSingleByte) 
  813.                 {
  814.                     offset = offset + 1;
  815.                 }
  816.             }
  817.             break;
  818.  
  819.         case kGoUp: 
  820.             WEGetPoint(offset, &thePoint, &lineHeight, hWE);
  821.             thePoint.v = thePoint.v - 1;
  822.             offset = WEGetOffset(&thePoint, &edge, hWE);
  823.             break;
  824.  
  825.         case kGoDown: 
  826.             WEGetPoint(offset, &thePoint, &lineHeight, hWE);
  827.             thePoint.v = thePoint.v + lineHeight;
  828.             offset = WEGetOffset(&thePoint, &edge, hWE);
  829.             break;
  830.             
  831.         case kGoWordStart: 
  832.             WEFindWord(offset, kTrailingEdge, &rangeStart, &rangeEnd, hWE);
  833.             offset = rangeStart;
  834.             break;
  835.             
  836.         case kGoWordEnd: 
  837.             WEFindWord(offset, kLeadingEdge, &rangeStart, &rangeEnd, hWE);
  838.             offset = rangeEnd;
  839.             break;
  840.             
  841.         case kGoTextStart: 
  842.             offset = 0;
  843.             break;
  844.  
  845.         case kGoTextEnd: 
  846.             offset = textLength;
  847.             break;
  848.  
  849.         case kGoLineStart: 
  850.             WEFindLine(offset, kLeadingEdge, &rangeStart, &rangeEnd, hWE);
  851.             offset = rangeStart;
  852.             break;
  853.  
  854.         case kGoLineEnd: 
  855.             WEFindLine(offset, kTrailingEdge, &rangeStart, &rangeEnd, hWE);
  856.             offset = rangeEnd;
  857.             if (offset < textLength) 
  858.             {
  859.                 offset = offset - 1;
  860.                 if (WECharByte(offset, hWE) != smSingleByte) 
  861.                 {
  862.                             offset = offset - 1;
  863.                 }
  864.             }
  865.             break;
  866.             
  867.         default:
  868.             break;
  869.     }
  870.  
  871.     return offset;
  872. }
  873.  
  874. void _WEDoArrowKey (short arrow, short modifiers, WEHandle hWE)
  875. {
  876.     // { this routine is called by WEKey to handle arrow keys }
  877.     // { the WE record is guaranteed to be already locked }
  878.  
  879.     WEPtr pWE;
  880.     short action;
  881.     long selStart, selEnd;
  882.     long caretLoc, anchor;
  883.  
  884.     pWE = *hWE;
  885.  
  886.     // { calculate the "action" parameter for _WEArrowOffset from arrow and modifiers }
  887.     action = arrow - kArrowLeft;            // { possible range: 0..3 }
  888.     if (modifiers & optionKey)
  889.     {
  890.         action = action + kOption;
  891.     }
  892.     if (modifiers & cmdKey)
  893.     {
  894.         action = action + kCommand;
  895.     }
  896.     
  897.     // { get selection range }
  898.     selStart = pWE->selStart;
  899.     selEnd = pWE->selEnd;
  900.  
  901.     if ((modifiers & shiftKey) == 0) 
  902.     {
  903.         // { if selection range isn't empty, collapse it to one of the endpoints }
  904.         if (selStart < selEnd) 
  905.         {
  906.             if ((arrow == kArrowLeft) || (arrow == kArrowUp)) 
  907.             {
  908.                 caretLoc = selStart;
  909.             }
  910.             else
  911.             {
  912.                 caretLoc = selEnd;
  913.             }
  914.         }
  915.         else
  916.         {
  917.             // { otherwise move the insertion point }
  918.             caretLoc = _WEArrowOffset(action, selStart, hWE);
  919.         }
  920.         
  921.         // { set anchor to caretLoc, so new selection will be empty }
  922.         anchor = caretLoc;
  923.     }
  924.     else
  925.     {
  926.         // { shift key was held down: extend the selection rather than replacing it }
  927.         // { find out which selection boundary is the anchor and which is the free endpoint }
  928.         if (BTST(pWE->flags, weFAnchorIsEnd)) 
  929.         {
  930.             anchor = selEnd;
  931.             caretLoc = selStart;
  932.         }
  933.         else
  934.         {
  935.             anchor = selStart;
  936.             caretLoc = selEnd;
  937.         }
  938.         
  939.         // { move the free endpoint }
  940.         caretLoc = _WEArrowOffset(action, caretLoc, hWE);
  941.     }
  942.  
  943.     // { select the new selection }
  944.     WESetSelection(anchor, caretLoc, hWE);
  945. }
  946.  
  947. pascal Boolean WEAdjustCursor(Point mouseLoc, RgnHandle mouseRgn, WEHandle hWE)
  948. {
  949.     // { Call WEAdjustCursor to set the cursor shape when the mouse is in the view rectangle. }
  950.     // { MouseRgn should be either a valid region handle or NIL. }
  951.     // { If mouseRgn is supplied (i.e., if it's not NIL), it is intersected with a region }
  952.     // { in global coordinates within which the cursor is to retain its shape. }
  953.     // { WEAdjustCursor returns TRUE if the cursor has been set. }
  954.     // { Your application should set the cursor only if WEAdjustCursor returns FALSE. }
  955.  
  956.     WEPtr pWE;
  957.     RgnHandle auxRgn;
  958.     Point portDelta;
  959.     GrafPtr savePort;
  960.     Boolean saveWELock;
  961.     Boolean adjustCursor;
  962.     Point zeroPoint = {0, 0};
  963.  
  964.     adjustCursor = false;
  965.  
  966.     // { lock the WE record }
  967.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  968.     pWE = *hWE;
  969.  
  970.     // { set up the port }
  971.     GetPort(&savePort);
  972.     SetPort(pWE->port);
  973.  
  974.     // { calculate delta between the local coordinate system and the global one }
  975.     portDelta = zeroPoint;
  976.     LocalToGlobal(&portDelta);
  977.  
  978.     // { calculate the visible portion of the view rectangle, in global coordinates }
  979.     auxRgn = NewRgn();
  980.     CopyRgn(pWE->viewRgn, auxRgn);
  981.     SectRgn(auxRgn, pWE->port->visRgn, auxRgn);
  982.     OffsetRgn(auxRgn, portDelta.h, portDelta.v);
  983.  
  984.     if (PtInRgn(mouseLoc, auxRgn)) 
  985.     {
  986.         // { mouse is within view rectangle: it's up to us to set the cursor }
  987.         adjustCursor = true;
  988.  
  989.         // { set the cursor to an I-beam }
  990.         SetCursor(*GetCursor(iBeamCursor));
  991.  
  992.         // { set mouseRgn, if provided }
  993.         if (mouseRgn != nil) 
  994.         {
  995.             SectRgn(mouseRgn, auxRgn, mouseRgn);
  996.         }
  997.     }
  998.     else
  999.     {
  1000.         // { mouse is outside view rectangle: don't set the cursor; subtract viewRgn from mouseRgn }
  1001.         if (mouseRgn != nil) 
  1002.         {
  1003.             DiffRgn(mouseRgn, auxRgn, mouseRgn);
  1004.         }
  1005.     }
  1006.     // { dispose of the temporary region }
  1007.     DisposeRgn(auxRgn);
  1008.  
  1009.     // { restore the port }
  1010.     SetPort(savePort);
  1011.  
  1012.     // { unlock the WE record }
  1013.     _WESetHandleLock((Handle)hWE, saveWELock);
  1014.     
  1015.     return adjustCursor;
  1016. }
  1017.  
  1018. pascal void WEIdle(long *maxSleep, WEHandle hWE)
  1019. {
  1020.     WEPtr pWE;
  1021.     long caretInterval, sleepTime;
  1022.     Boolean saveWELock;
  1023.  
  1024.     // { lock the WE record }
  1025.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  1026.     pWE = *hWE;
  1027.  
  1028.     // { the caret blinks only if we're active and the selection point is empty }
  1029.     if (BTST(pWE->flags, weFActive) && (pWE->selStart == pWE->selEnd)) 
  1030.     {
  1031.         // { the low-memory global variable CaretTime contains the preferred interval }
  1032.         // { between successive inversions of the caret }
  1033.         caretInterval = GetCaretTime();
  1034.  
  1035.         // { calculate how many ticks we can sleep before we need to invert the caret }
  1036.         // { the caretTime field of the WE record contains the time of the last inversion }
  1037.         sleepTime = caretInterval - (TickCount() - pWE->caretTime);
  1038.  
  1039.         // { if sleepTime has gone negative, invert the caret }
  1040.         if (sleepTime <= 0) 
  1041.         {
  1042.             _WEDrawCaret(hWE);
  1043.             sleepTime = caretInterval;
  1044.         }
  1045.     }
  1046.     else
  1047.     {
  1048.         // { if we don't need to blink the caret, we can sleep forever }
  1049.         sleepTime = LONG_MAX;
  1050.     }
  1051.     
  1052.     // { return sleepTime to the caller if maxSleep isn't NIL }
  1053.     if (maxSleep != nil)
  1054.     {
  1055.         *maxSleep = sleepTime;
  1056.     }
  1057.     
  1058.     // { unlock the WE record }
  1059.     _WESetHandleLock((Handle)hWE, saveWELock);
  1060. }
  1061.  
  1062. pascal void WEUpdate(RgnHandle updateRgn, WEHandle hWE)
  1063. {
  1064.     WEPtr pWE;
  1065.     long firstLine, lastLine;
  1066.     Rect auxRect;
  1067.     RgnHandle saveClip, auxRgn;
  1068.     GrafPtr savePort;
  1069.     Boolean saveWELock;
  1070.  
  1071.     // { lock the WE record }
  1072.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  1073.     pWE = *hWE;
  1074.  
  1075.     // { set up the port }
  1076.     GetPort(&savePort);
  1077.     SetPort(pWE->port);
  1078.  
  1079.     // { save the clip region }
  1080.     saveClip = NewRgn();
  1081.     GetClip(saveClip);
  1082.  
  1083.     // { clip to the insersection between updateRgn and the view rectangle }
  1084.     // { (updateRgn may be NIL; in this case, just clip to the view rectangle) }
  1085.     auxRgn = NewRgn();
  1086.     if (updateRgn != nil) 
  1087.     {
  1088.         SectRgn(updateRgn, pWE->viewRgn, auxRgn);
  1089.     }
  1090.     else
  1091.     {
  1092.         CopyRgn(pWE->viewRgn, auxRgn);
  1093.     }
  1094.     SetClip(auxRgn);
  1095.  
  1096.     if (EmptyRgn(auxRgn) == false) 
  1097.     {
  1098.         // { set auxRect to the bounding box of the update region (clipped to the view rectangle) }
  1099.         auxRect = (*auxRgn)->rgnBBox;
  1100.  
  1101.         // { find out which lines need to be redrawn }
  1102.         firstLine = _WEPixelToLine(auxRect.top - pWE->destRect.top, hWE);
  1103.         lastLine = _WEPixelToLine((auxRect.bottom - 1) - pWE->destRect.top, hWE);
  1104.  
  1105.         // { draw them (if updateRgn is NIL, erase each line rectangle before redrawing) }
  1106.         _WEDrawLines(firstLine, lastLine, (updateRgn == nil), hWE);
  1107.  
  1108.         // { hilite the selection range or draw the caret (only if active) }
  1109.         if (pWE->selStart < pWE->selEnd) 
  1110.             _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1111.         else if (BTST(pWE->flags, weFCaretVisible)) 
  1112.         {
  1113.             _WEDrawCaret(hWE);
  1114.             BSET(pWE->flags, weFCaretVisible);
  1115.         }
  1116.     }
  1117.  
  1118.     DisposeRgn(auxRgn);
  1119.  
  1120.     // { restore the clip region }
  1121.     SetClip(saveClip);
  1122.     DisposeRgn(saveClip);
  1123.  
  1124.     // { restore the port }
  1125.     SetPort(savePort);
  1126.  
  1127.     // { unlock the WE record }
  1128.     _WESetHandleLock((Handle)hWE, saveWELock);
  1129. }
  1130.  
  1131. pascal void WEDeactivate(WEHandle hWE)
  1132. {
  1133.     WEPtr pWE;
  1134.     Boolean saveWELock;
  1135.  
  1136.     if (!WEIsActive(hWE)) return;
  1137.  
  1138.     // { lock the WE record }
  1139.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  1140.     pWE = *hWE;
  1141.  
  1142.     // { hide the selection range or the caret }
  1143.     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1144.     if (BTST(pWE->flags, weFCaretVisible))
  1145.     {
  1146.         _WEDrawCaret(hWE);
  1147.     }
  1148.     
  1149.     // { clear the active flag }
  1150.     BCLR(pWE->flags, weFActive);
  1151.  
  1152.     // { frame the selection }
  1153.     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1154.  
  1155.     // { dispose of the offscreen graphics world, if any }
  1156.     if (pWE->offscreenPort != nil) 
  1157.     {
  1158.         DisposeGWorld((GWorldPtr)(pWE->offscreenPort));
  1159.         pWE->offscreenPort = nil;
  1160.     }
  1161.  
  1162.     // { notify Text Services }
  1163.     if (pWE->tsmReference != nil) 
  1164.     {
  1165.         DeactivateTSMDocument(pWE->tsmReference);
  1166.     }
  1167.     
  1168.     // { unlock the WE record }
  1169.     _WESetHandleLock((Handle)hWE, saveWELock);
  1170. }
  1171.  
  1172. pascal void WEActivate(WEHandle hWE)
  1173. {
  1174.     WEPtr pWE;
  1175.     Boolean saveWELock;
  1176.  
  1177.     if (WEIsActive(hWE)) return;
  1178.  
  1179.     // { lock the WE record }
  1180.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  1181.     pWE = *hWE;
  1182.  
  1183.     // { remove the selection frame }
  1184.     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1185.  
  1186.     // { set the active flag }
  1187.     BSET(pWE->flags, weFActive);
  1188.  
  1189.     // { show the selection range }
  1190.     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1191.  
  1192.     // { notify Text Services }
  1193.     if (pWE->tsmReference != nil) 
  1194.     {
  1195.         ActivateTSMDocument(pWE->tsmReference);
  1196.     }
  1197.     
  1198.     // { unlock the WE record }
  1199.     _WESetHandleLock((Handle)hWE, saveWELock);
  1200. }
  1201.  
  1202. pascal Boolean WEIsActive(WEHandle hWE)
  1203. {
  1204.     // { return TRUE iff the specified WE instance is currently active }
  1205.     return BTST((*hWE)->flags, weFActive);
  1206. }
  1207.  
  1208. pascal void WEScroll(long hOffset, long vOffset, WEHandle hWE)
  1209. {
  1210.     WEPtr pWE;
  1211.     Rect viewRect;
  1212.     RgnHandle updateRgn;
  1213.     GrafPtr savePort;
  1214.     Boolean saveWELock;
  1215.  
  1216.     // { lock the WE record }
  1217.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  1218.     pWE = *hWE;
  1219.  
  1220.     // { hide the caret if it's showing }
  1221.     if (BTST(pWE->flags, weFCaretVisible))
  1222.     {
  1223.         _WEDrawCaret(hWE);
  1224.     }
  1225.     
  1226.     // { offset the destination rectangle by the specified amount }
  1227.     WEOffsetLongRect(&pWE->destRect, hOffset, vOffset);
  1228.  
  1229.     // { set up the port }
  1230.     GetPort(&savePort);
  1231.     SetPort(pWE->port);
  1232.  
  1233.     viewRect = (*pWE->viewRgn)->rgnBBox;
  1234.     updateRgn = NewRgn();
  1235.  
  1236.     // { scroll the view rectangle }
  1237.     ScrollRect(&viewRect, hOffset, vOffset, updateRgn);
  1238.  
  1239.     // { redraw the exposed region }
  1240.     WEUpdate(updateRgn, hWE);
  1241.     DisposeRgn(updateRgn);
  1242.  
  1243.     // { restore the port }
  1244.     SetPort(savePort);
  1245.  
  1246.     // { unlock the WE record }
  1247.     _WESetHandleLock((Handle)hWE, saveWELock);
  1248. }
  1249.  
  1250. Boolean _WEScrollIntoView (long offset, WEHandle hWE)
  1251. {
  1252.     WEPtr pWE;
  1253.     LongPt thePoint;
  1254.     short lineHeight;
  1255.     long hScroll, vScroll, temp;
  1256.     Boolean retval;
  1257.     
  1258.     pWE = *hWE;
  1259.  
  1260.     // { do nothing if automatic scrolling is disabled }
  1261.     if (!BTST(pWE->flags, weFAutoScroll)) 
  1262.     {
  1263.         return false;
  1264.     }
  1265.  
  1266.     // { find the selection point }
  1267.     WEGetPoint(offset, &thePoint, &lineHeight, hWE);
  1268.  
  1269.     // { assume no scrolling is needed }
  1270.     retval = false;
  1271.     vScroll = 0;
  1272.     hScroll = 0;
  1273.  
  1274.     // { determine if we need to scroll vertically }
  1275.     if ((thePoint.v < pWE->viewRect.top) || 
  1276.         (thePoint.v + lineHeight >= pWE->viewRect.bottom))
  1277.     {
  1278.         // { calculate the amount of vertical scrolling needed to center the selection into view }
  1279.         vScroll = BSR(pWE->viewRect.top + pWE->viewRect.bottom, 1) - thePoint.v;
  1280.  
  1281.         // { we'd like to superimpose the bottom margins of the dest/view rects, if possible }
  1282.         temp = pWE->viewRect.bottom - pWE->destRect.bottom;
  1283.         if (temp > vScroll) 
  1284.         {
  1285.             vScroll = temp;
  1286.         }
  1287.         // { but we also have to make sure the dest top isn't scrolled below the view top }
  1288.         temp = pWE->viewRect.top - pWE->destRect.top;
  1289.         if (temp < vScroll) 
  1290.         {
  1291.             vScroll = temp;
  1292.         }
  1293.     }
  1294.     
  1295.     // { determine if we need to scroll horizontally }
  1296.     if ((thePoint.h - 1 < pWE->viewRect.left) || (thePoint.h >= pWE->viewRect.right))
  1297.     { 
  1298.         // { calculate the amount of horizontal scrolling needed to center the selection into view }
  1299.         hScroll = BSR(pWE->viewRect.left + pWE->viewRect.right, 1) - thePoint.h;
  1300.  
  1301.         // { we'd like to superimpose the right margins of the dest/view rects, if possible }
  1302.         temp = pWE->viewRect.right - pWE->destRect.right;
  1303.         if (temp > hScroll) 
  1304.         {
  1305.             hScroll = temp;
  1306.         }
  1307.         
  1308.         // { but we also have to make sure the dest left isn't scrolled to the right of the view left }
  1309.         temp = pWE->viewRect.left - pWE->destRect.left;
  1310.         if (temp < hScroll) 
  1311.         {
  1312.             hScroll = temp;
  1313.         }
  1314.     }
  1315.     
  1316.     // { scroll the text if necessary }
  1317.     if ((vScroll != 0) || (hScroll != 0)) 
  1318.     {
  1319.         retval = true;
  1320.         WEScroll(hScroll, vScroll, hWE);
  1321.     }
  1322.  
  1323.     // { call the scroll callback, if any }
  1324.     if (pWE->scrollProc != nil) 
  1325.     {
  1326.         ((WEScrollProcPtr)pWE->scrollProc)(hWE);
  1327.     }
  1328.     return retval;
  1329. }
  1330.  
  1331. pascal void WESelView(WEHandle hWE)
  1332. {
  1333.     WEPtr pWE;
  1334.     long offset;
  1335.     Boolean saveWELock;
  1336.  
  1337.     // { lock the WE record }
  1338.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  1339.     pWE = *hWE;
  1340.  
  1341.     // { scroll the free endpoint of the selection into view }
  1342.     if (BTST(pWE->flags, weFAnchorIsEnd)) 
  1343.     {
  1344.         offset = pWE->selStart;
  1345.     }
  1346.     else
  1347.     {
  1348.         offset = pWE->selEnd;
  1349.     }
  1350.     _WEScrollIntoView(offset, hWE);
  1351.  
  1352.     // { unlock the WE record }
  1353.     _WESetHandleLock((Handle)hWE, saveWELock);
  1354. }
  1355.